<script setup lang="ts">
import { onMounted, onUnmounted, reactive, ref } from 'vue'
import TheWindow from './TheWindow.vue'
import TheCursor from '../canvas/cursor/TheCursor.vue'
import {
  minWindowWidth,
  minWindowHeight,
  type BaseWindow,
  type Layout,
} from './class/basewindow'
import {
  WindowSeparatorLine,
  windowSeparatorLineWidth,
} from './class/windowseparatorline'
import WindowSeparatorLineVue from './WindowSeparatorLine.vue'
import { WindowControlArea } from './class/windowcontrolarea'
import WindowControlAreaVue from './WindowControlArea.vue'
import { windows, windowLines, windowControlAreas } from './ts/windowmanager'
import WindowMergeAreaVue from './WindowMergeArea.vue'
import WindowMaskAreaVue from './WindowMaskArea.vue'
import type { MergeInfo, WindowMergeArea } from './class/windowmergearea'
import type { CursorType } from '../canvas/cursor/cursor'

/** 当前id。布局、窗口使用 */
let currId = 1
/** 获取下一个id */
function getNextId() {
  return currId++
}

const layout = reactive<Layout>({
  flow: 'Horizontal',
  separator: [0.3, 0.7],
  children: [
    { category: 'view3d' },
    { category: 'view3d' },
    {
      flow: 'Vertical',
      separator: [0.4, 0.6],
      children: [
        { category: 'view3d' },
        { category: 'view3d' },
        { category: 'view3d' },
      ],
    },
  ],
})

const $container = ref()

// 计算布局/窗口的位置尺寸，并填充窗口（分割线/控制区域）数组
function fillWindows(
  layout: Layout,
  windows: Array<BaseWindow>,
  lines: Array<WindowSeparatorLine> | undefined,
  areas: Array<WindowControlArea> | undefined,
  rect: DOMRect,
  isWholeResize = false // 是否是因为整个窗口大小变化引起的调整
) {
  layout.rect = rect
  if (!layout.parent) {
    layout.top = 0
    layout.bottom = rect.height
    layout.left = 0
    layout.right = rect.width
  }
  if (layout.flow === 'Horizontal') {
    // 横向
    if (layout.children && layout.children.length) {
      layout.children.forEach((item, index) => {
        item.parent = layout
        item.index = index
        item.rect = rect
        item.top = layout.top
        item.bottom = layout.bottom
        const l = layout.left as number
        const r = layout.right as number
        if (index === 0) {
          // 如果是第一个
          item.left = layout.left
        } else {
          // 如果不是第一个
          if (isWholeResize || item.left === undefined) {
            // 如果是整体缩放 或 初次赋值
            item.left =
              l + (r - l) * layout.separator[index - 1] + halfLineWidth
          }
          if (lines) {
            lines.push(
              new WindowSeparatorLine(
                'Vertical',
                item.top as number,
                item.bottom as number,
                item.left - 2,
                item.left,
                layout,
                index - 1
              )
            )
          }
        }
        if (index === layout.children.length - 1) {
          // 如果是最后一个
          item.right = layout.right
        } else {
          // 如果不是最后一个
          if (isWholeResize || item.right === undefined) {
            // 如果是整体缩放 或 初次赋值
            item.right = l + (r - l) * layout.separator[index] - halfLineWidth
          }
        }
        if (isLayout(item)) {
          // 如果是布局
          fillWindows(item, windows, lines, areas, rect, isWholeResize)
        } else {
          // 如果是窗口
          windows.push(item)
          if (!item.id) {
            // 如果没有id，就生成id
            item.id = getNextId()
          }
          if (areas) {
            areas.push(new WindowControlArea(item, 'left'))
            areas.push(new WindowControlArea(item, 'right'))
          }
        }
      })
    }
  } else {
    // 纵向
    if (layout.children && layout.children.length) {
      layout.children.forEach((item, index) => {
        item.parent = layout
        item.index = index
        item.rect = rect
        item.left = layout.left
        item.right = layout.right
        const t = layout.top as number
        const b = layout.bottom as number
        if (index === 0) {
          // 如果是第一个
          item.top = layout.top
        } else {
          // 如果不是第一个
          if (isWholeResize || item.top === undefined) {
            // 如果是整体缩放 或 初次赋值
            item.top = t + (b - t) * layout.separator[index - 1] + halfLineWidth
          }
          if (lines) {
            lines.push(
              new WindowSeparatorLine(
                'Horizontal',
                item.top - 2,
                item.top,
                item.left as number,
                item.right as number,
                layout,
                index - 1
              )
            )
          }
        }
        if (index === layout.children.length - 1) {
          // 如果是最后一个
          item.bottom = layout.bottom
        } else {
          // 如果不是最后一个
          if (isWholeResize || item.bottom === undefined) {
            // 如果是整体缩放 或 初次赋值
            item.bottom = t + (b - t) * layout.separator[index] - halfLineWidth
          }
        }
        if (isLayout(item)) {
          // 如果是布局
          fillWindows(item, windows, lines, areas, rect, isWholeResize)
        } else {
          // 如果是窗口
          windows.push(item)
          if (!item.id) {
            // 如果没有id，就生成id
            item.id = getNextId()
          }
          if (areas) {
            areas.push(new WindowControlArea(item, 'left'))
            areas.push(new WindowControlArea(item, 'right'))
          }
        }
      })
    }
  }
}

// 是否是布局
function isLayout(lw: Layout | BaseWindow): lw is Layout {
  return (lw as Layout).flow !== undefined
}

// 重新布局
function doLayout() {
  const rect = ($container.value as HTMLElement).getBoundingClientRect()
  const wins: Array<BaseWindow> = []
  const lines: Array<WindowSeparatorLine> = []
  const areas: Array<WindowControlArea> = []
  fillWindows(layout, wins, lines, areas, rect)
  windows.value = wins
  windowLines.value = lines
  windowControlAreas.value = areas
}

// 因窗口大小变化而重新布局
function doLayoutOnResize() {
  const rect = ($container.value as HTMLElement).getBoundingClientRect()
  const wins: Array<BaseWindow> = []
  const lines: Array<WindowSeparatorLine> = []
  const areas: Array<WindowControlArea> = []
  fillWindows(layout, wins, lines, areas, rect, true)
  windows.value = wins
  windowLines.value = lines
  windowControlAreas.value = areas
}

// 重新布局但仅更新窗口与分割线
function doLayoutWindowsAndLines() {
  const rect = ($container.value as HTMLElement).getBoundingClientRect()
  const wins: Array<BaseWindow> = []
  const lines: Array<WindowSeparatorLine> = []
  fillWindows(layout, wins, lines, undefined, rect)
  windows.value = wins
  windowLines.value = lines
}

// 重新布局但仅更新窗口
function doLayoutOnlyWindows() {
  const rect = ($container.value as HTMLElement).getBoundingClientRect()
  const wins: Array<BaseWindow> = []
  fillWindows(layout, wins, undefined, undefined, rect)
  windows.value = wins
}

const halfLineWidth = windowSeparatorLineWidth / 2 // 线宽的一半，用来调整线的中间位置
// 分割线移动，调整布局的分割线百分比，以及布局的节点位置
function moveLine(line: WindowSeparatorLine, offsetX: number, offsetY: number) {
  const layout = line.layout
  const index = line.index
  const top = layout.top as number
  const bottom = layout.bottom as number
  const left = layout.left as number
  const right = layout.right as number
  if (layout.flow === 'Horizontal') {
    // 如果是水平布局
    const currX = line.left + halfLineWidth + offsetX
    layout.separator[index] = (currX - left) / (right - left)
    layout.children[index].right = currX - halfLineWidth
    layout.children[index + 1].left = currX + halfLineWidth
  } else {
    // 另一种个情况则是竖直布局
    const currY = line.top + halfLineWidth + offsetY
    layout.separator[index] = (currY - top) / (bottom - top)
    layout.children[index].bottom = currY - halfLineWidth
    layout.children[index + 1].top = currY + halfLineWidth
  }
  updateLayoutSeparator(layout.children[index], layout.flow)
  updateLayoutSeparator(layout.children[index + 1], layout.flow)
}

function updateLayoutSeparator(
  layout: Layout | BaseWindow,
  flow: 'Horizontal' | 'Vertical'
) {
  if (isLayout(layout)) {
    if (layout.flow === flow) {
      // 如果布局方向相同，那么才会受到影响
      if (layout.flow === 'Horizontal') {
        // 如果是水平布局
        const left = layout.left as number // 布局左边界
        const right = layout.right as number // 布局右边界
        const width = right - left // 布局宽度
        layout.children[0].left = left // 调整第一个子元素的左边界
        layout.children[layout.children.length - 1].right = right // 调整最后一个子元素的右边界
        layout.children.forEach((item, index) => {
          if (index > 0) {
            // 从第二个开始
            layout.separator[index - 1] =
              ((item.left as number) - halfLineWidth - left) / width
            // console.log(layout.separator[index - 1])
          }
        })
      } else {
        // 如果是垂直布局
        const top = layout.top as number // 布局上边界
        const bottom = layout.bottom as number // 布局下边界
        const height = bottom - top // 布局高度
        layout.children[0].top = top // 调整第一个子元素的上边界
        layout.children[layout.children.length - 1].bottom = bottom // 调整最后一个子元素的下边界
        layout.children.forEach((item, index) => {
          if (index > 0) {
            // 从第二个开始
            layout.separator[index - 1] =
              ((item.top as number) - halfLineWidth - top) / height
            // console.log(layout.separator[index - 1])
          }
        })
      }
    }
    layout.children.forEach(item => {
      updateLayoutSeparator(item, flow)
    })
  }
}

// 拖动分割线后调用
function dragLine(line: WindowSeparatorLine, offsetX: number, offsetY: number) {
  moveLine(line, offsetX, offsetY)
  doLayoutOnlyWindows()
}

// 拖动后释放分割线调用
function releaseLine(
  line: WindowSeparatorLine,
  offsetX: number,
  offsetY: number
) {
  moveLine(line, offsetX, offsetY)
  doLayout()
}

onMounted(() => {
  doLayoutOnResize()
  window.addEventListener('resize', doLayoutOnResize)
})

onUnmounted(() => {
  window.removeEventListener('resize', doLayoutOnResize)
})

/** 松开拖拽控制区域 */
function releaseControlArea() {
  if (mergeInfo.value.state === 2) {
    // 需要合并
    mergeWindow()
  }
  hideMergeBox()
  doLayout()
  cursorType.value = 'default'
}

// 拖出新窗口
function addWindow(
  data: WindowControlArea,
  direction: 'bottom' | 'left' | 'right',
  startXY: number
) {
  const layout = data.window.parent as Layout
  const index = data.window.index as number // 窗口序数
  const flow = direction === 'bottom' ? 'Vertical' : 'Horizontal'
  const top = layout.top as number
  const bottom = layout.bottom as number
  const left = layout.left as number
  const right = layout.right as number
  const width = right - left
  const height = bottom - top
  if (layout.flow === flow) {
    // 如果与布局朝向相同
    let separator
    if (direction === 'left') {
      // 如果是向左拖出
      separator = layout.separator[index] - minWindowWidth / width
      data.startX = startXY
    } else if (direction === 'right') {
      // 如果是向右拖出
      separator = (layout.separator[index - 1] || 0) + minWindowWidth / width
      data.startX = startXY
    } else {
      // 如果是向下拖出
      separator = (layout.separator[index - 1] || 0) + minWindowHeight / height
      data.startY = startXY
    }
    layout.separator.splice(index, 0, separator)
    const win = layout.children[index]
    // 删除大小信息后，doLayoutOnlyWindows会重新填充
    delete win.top
    delete win.bottom
    delete win.left
    delete win.right
    layout.children.splice(index, 0, {
      category: data.window.category,
      id: getNextId(),
    })
  } else {
    // 如果与布局朝向不同
    let separator
    if (direction === 'left') {
      // 如果是向左拖出
      separator = 1 - minWindowWidth / width
      data.startX = startXY
    } else if (direction === 'right') {
      // 如果是向右拖出
      separator = minWindowWidth / width
      data.startX = startXY
    } else {
      // 如果是向下拖出
      separator = minWindowHeight / height
      data.startY = startXY
    }
    // data.index = 0
    const win = layout.children[index]
    // 删除大小信息后，doLayoutOnlyWindows会重新填充
    delete win.top
    delete win.bottom
    delete win.left
    delete win.right
    layout.children.splice(index, 1, {
      flow,
      separator: [separator],
      children: [win, { category: data.window.category, id: getNextId() }],
    })
  }
  doLayoutWindowsAndLines()
  // console.log(layout, height, minWindowHeight)
  // debugger
  // console.log('addWindow', data)
}

// 拖动创建新窗口后继续拖动控制区域
function dragArea(area: WindowControlArea, offsetX: number, offsetY: number) {
  const layout = area.window.parent as Layout
  const index = Math.max(0, (area.window.index as number) - 1) // 拖出新布局时，减一后是-1
  const top = layout.top as number
  const bottom = layout.bottom as number
  const left = layout.left as number
  const right = layout.right as number
  // console.log(layout.flow)
  if (layout.flow === 'Horizontal') {
    // 如果是水平布局
    const currX = area.startX + halfLineWidth + offsetX
    // console.log('currX: ', currX, offsetX)
    layout.separator[index] = (currX - left) / (right - left)
    layout.children[index].right = currX - halfLineWidth
    layout.children[index + 1].left = currX + halfLineWidth
  } else {
    // 另一种个情况则是竖直布局
    const currY = area.startY + halfLineWidth + offsetY
    // console.log('currY: ', currY)
    layout.separator[index] = (currY - top) / (bottom - top)
    layout.children[index].bottom = currY - halfLineWidth
    layout.children[index + 1].top = currY + halfLineWidth
  }
  doLayoutWindowsAndLines()
}

const mergeWindowVisible = ref(false)
const mergeWindowInfo = ref<WindowMergeArea>({
  top: 0,
  bottom: 0,
  left: 0,
  right: 0,
})
const mergeInfo = ref<MergeInfo>({
  src: undefined,
  dst: undefined,
  state: 0,
})
const cursorType = ref<CursorType>('default')

/** 显示合并窗口 */
function showMergeBox(win: BaseWindow, targetWin: BaseWindow) {
  if (win.parent) {
    mergeWindowVisible.value = true
    const top = Math.min(win.top as number, targetWin.top as number)
    const bottom = Math.max(win.bottom as number, targetWin.bottom as number)
    const left = Math.min(win.left as number, targetWin.left as number)
    const right = Math.max(win.right as number, targetWin.right as number)
    mergeWindowInfo.value = {
      top,
      bottom: (win.rect?.height as number) - bottom,
      left,
      right: (win.rect?.width as number) - right,
    }

    mergeInfo.value.state = 2
    mergeInfo.value.src = win
    mergeInfo.value.dst = targetWin
    mergeInfo.value.top = top
    mergeInfo.value.bottom = bottom
    mergeInfo.value.left = left
    mergeInfo.value.right = right

    // 判断光标形状
    const srcLeft = mergeInfo.value.src.left as number
    const dstLeft = mergeInfo.value.dst.left as number
    const srcTop = mergeInfo.value.src.top as number
    const dstTop = mergeInfo.value.dst.top as number
    if (Math.abs(srcLeft - dstLeft) < minWindowWidth) {
      // 如果两窗口的起点位置小于1个最小窗口的大小，那么表示两窗口是在同一竖直方向上
      if (srcTop < dstTop) {
        // 如果原窗口上部小于目标窗口上部，那么表示向下合并
        cursorType.value = 'down'
      } else {
        // 反之，则表示向上合并
        cursorType.value = 'up'
      }
    } else {
      // 反之，则是在同一水平方向上
      if (srcLeft < dstLeft) {
        // 如果原窗口左侧小于目标窗口左侧，那么表示向右合并
        cursorType.value = 'right'
      } else {
        // 反之，则表示向左合并
        cursorType.value = 'left'
      }
    }
  }
}

/** 隐藏合并窗口 */
function hideMergeBox(changeCursor = false) {
  mergeWindowVisible.value = false
  mergeInfo.value.state = 0
  if (changeCursor) {
    cursorType.value = 'no'
  }
}

/** 合并窗口 */
function mergeWindow() {
  const win = mergeInfo.value.src as BaseWindow
  const targetWin = mergeInfo.value.dst as BaseWindow
  if (win.parent === targetWin.parent) {
    // 如果是同一个布局
    const parent = win.parent as Layout
    if (parent.children.length === 2) {
      // 如果只有两个子元素
      if (parent.parent) {
        // 如果还有上层，表示不是最上层，则移除该布局，变成一个窗口
        const topParent = parent.parent as Layout
        topParent.children[parent.index as number] = win
        win.top = parent.top
        win.bottom = parent.bottom
        win.left = parent.left
        win.right = parent.right
        win.parent = topParent
      } else {
        // 如果没有上层，表示已经是最上层了，则仅合并窗口，使布局中仅剩一个窗口
        parent.separator.length = 0
        parent.children.splice(targetWin.index as number, 1) // 删去被合并掉的窗口
        win.top = mergeInfo.value.top
        win.bottom = mergeInfo.value.bottom
        win.left = mergeInfo.value.left
        win.right = mergeInfo.value.right
      }
    } else {
      // 如果有两个以上子元素，则仅合并窗口
      const separatorIndex = Math.min(
        win.index as number,
        targetWin.index as number
      )
      parent.separator.splice(separatorIndex, 1) // 删去分割区域线
      parent.children.splice(targetWin.index as number, 1) // 删去被合并掉的窗口
      win.top = mergeInfo.value.top
      win.bottom = mergeInfo.value.bottom
      win.left = mergeInfo.value.left
      win.right = mergeInfo.value.right
    }
  }
}

const cursorUrl = ref('')
function onCursorChange(url: string) {
  cursorUrl.value = url
}

function getRelativeWindows(line: WindowSeparatorLine) {}
</script>

<template>
  <div class="window-container" ref="$container">
    <the-window
      v-for="item in windows"
      :key="item.id"
      :data="item"
      :id="item.id"
    ></the-window>
    <window-control-area-vue
      v-for="item in windowControlAreas"
      :key="item.id"
      :data="item"
      :cursor-url="cursorUrl"
      @on-add-window="addWindow"
      @on-drag="dragArea"
      @on-show-merge-box="showMergeBox"
      @on-hide-merge-box="hideMergeBox(true)"
      @on-release-control-area="releaseControlArea"
    ></window-control-area-vue>
    <window-separator-line-vue
      v-for="item in windowLines"
      :key="item.id"
      :data="item"
      @on-drag="dragLine"
      @on-release="releaseLine"
    ></window-separator-line-vue>
    <window-merge-area-vue
      :visible="mergeWindowVisible"
      :data="mergeWindowInfo"
    ></window-merge-area-vue>
    <window-mask-area-vue
      :data="mergeInfo.dst"
      :visible="mergeWindowVisible"
    ></window-mask-area-vue>
    <the-cursor
      :type="cursorType"
      @on-cursor-change="onCursorChange"
    ></the-cursor>
  </div>
</template>

<style scoped>
.window-container {
  position: absolute;
  top: 2.1875rem;
  bottom: 0;
  left: 0;
  right: 0;
  overflow: hidden;
}
</style>
